﻿using System;
using System.Linq;

using Ninject;

using SSTU.Contract.Common;
using SSTU.Contract.Facade;
using SSTU.Contract.Interaction;
using SSTU.Extensions;
using SSTU.Management.Dependency;
using SSTU.Model;
using SSTU.ViewModel.Core;
using SSTU.ViewModel.Item;
using System.Globalization;

namespace SSTU.ViewModel
{
	public class GroupTimeTableViewModel : AppViewModelBase
	{
		private readonly IStructureFacade _structureFacade;
		private readonly ITimeTableFacade _timeTableFacade;

		private GroupItemVM _group;
		private PeriodicWeekItemVM _evenWeek;
		private PeriodicWeekItemVM _oddWeek;
		private DelegateCommand<GroupItemVM> _favoriteCommand;
		private string _weekTypeString;

		[Inject]
		public GroupTimeTableViewModel(
			IStructureFacade structureFacade,
			ITimeTableFacade timeTableFacade,
			INavigationInteraction navigationInteraction)
		{
			DependencyRequirement.Current.NotNullFromConstructorInjection<GroupTimeTableViewModel, IStructureFacade>(structureFacade);
			DependencyRequirement.Current.NotNullFromConstructorInjection<GroupTimeTableViewModel, ITimeTableFacade>(timeTableFacade);
			DependencyRequirement.Current.NotNullFromConstructorInjection<GroupTimeTableViewModel, INavigationInteraction>(navigationInteraction);
			_structureFacade = structureFacade;
			_timeTableFacade = timeTableFacade;
			NavigationInteraction = navigationInteraction;
			FavoriteCommand = new DelegateCommand<GroupItemVM>(ExecuteFavoriteCommand, CanExecuteFavoriteCommand);
			var calendar = System.Globalization.DateTimeFormatInfo.CurrentInfo.Calendar;
			int weekOfYear = calendar.GetWeekOfYear(DateTime.Now, CalendarWeekRule.FirstDay, DayOfWeek.Monday);
			// For specific university rules, now it should be on the contrary.
			//PeriodicWeekType currentWeekType = (weekOfYear % 2 == 0) ? PeriodicWeekType.Even : PeriodicWeekType.Odd;
			PeriodicWeekType currentWeekType = (weekOfYear % 2 == 0) ? PeriodicWeekType.Odd : PeriodicWeekType.Even;
			WeekTypeString = currentWeekType == PeriodicWeekType.Even ? "четная" : "нечетная";
		}

		/// <summary>
		/// Gets or sets current navigation interaction.
		/// </summary>
		public INavigationInteraction NavigationInteraction { get; set; }

		/// <summary>
		/// Gets current the group.
		/// </summary>
		public GroupItemVM Group
		{
			get { return _group; }
			protected set
			{
				if (value != _group)
				{
					RaisePropertyChanging<GroupItemVM>(() => Group);
					_group = value;
					RaisePropertyChanged<GroupItemVM>(() => Group);
				}
			}
		}

		/// <summary>
		/// Gets or sets the even week.
		/// </summary>
		public PeriodicWeekItemVM EvenWeek
		{
			get { return _evenWeek; }
			set
			{
				if (value != _evenWeek)
				{
					RaisePropertyChanging<PeriodicWeekItemVM>(() => EvenWeek);
					_evenWeek = value;
					RaisePropertyChanged<PeriodicWeekItemVM>(() => EvenWeek);
				}
			}
		}

		/// <summary>
		/// Gets or sets the odd week.
		/// </summary>
		public PeriodicWeekItemVM OddWeek
		{
			get { return _oddWeek; }
			set
			{
				if (value != _oddWeek)
				{
					RaisePropertyChanging<PeriodicWeekItemVM>(() => OddWeek);
					_oddWeek = value;
					RaisePropertyChanged<PeriodicWeekItemVM>(() => OddWeek);
				}
			}
		}

		/// <summary>
		/// Gets or sets the command for changing 'FavoriteState' of the group item.
		/// </summary>
		public DelegateCommand<GroupItemVM> FavoriteCommand
		{
			get { return _favoriteCommand; }
			set
			{
				if (value != _favoriteCommand)
				{
					RaisePropertyChanging<DelegateCommand<GroupItemVM>>(() => FavoriteCommand);
					_favoriteCommand = value;
					RaisePropertyChanged<DelegateCommand<GroupItemVM>>(() => FavoriteCommand);
				}
			}
		}

		/// <summary>
		/// Gets or sets the current periodic week type stirng.
		/// </summary>
		public string WeekTypeString
		{
			get { return _weekTypeString; }
			set
			{
				if (value != _weekTypeString)
				{
					RaisePropertyChanging<string>(() => WeekTypeString);
					_weekTypeString = value;
					RaisePropertyChanged<string>(() => WeekTypeString);
				}
			}
		}
		

		/// <summary>
		/// Gets or sets a value indicating whether in this instance time table from server is loaded.
		/// </summary>
		public bool IsTimeTableFromServerLoaded { get; set; }

		/// <summary>
		/// Initializes view model with the specified group item.
		/// </summary>
		public void Initialize(GroupItemVM groupItem)
		{
			Group = groupItem.Clone();
			Group.FavoriteState = FavoriteState.Unknown;
			EvenWeek = null;
			OddWeek = null;
			var groupId = groupItem.Id;
			IsTimeTableFromServerLoaded = false;
			UpdateIsBusy();
			_structureFacade.LoadGroupAsync(groupId, OnGroupLoaded);
			_timeTableFacade.LoadTimeTableForGroupAsync(groupId, response => OnGroupTimeTableLoaded(groupId, response));
		}

		/// <summary>
		/// Called when information about current group loaded.
		/// </summary>
		/// <param name="response">The facade response.</param>
		private void OnGroupLoaded(FacadeResponse<Group> response)
		{
			bool isSuccess = (response.IsSuccess && (response.Data != null));
			if (isSuccess)
			{
				bool canUpdate = ((Group != null) && (Group.Id == response.Data.Id));
				if (canUpdate)
				{
					Group.ShortName = response.Data.ShortName;
					Group.LongName = response.Data.LongName;
					Group.FavoriteState = response.Data.IsFavorite ? FavoriteState.Favorite : FavoriteState.NotFavorite;
				}
			}
			else
			{
				DisplayError(response.DataSource, "информации о группе", response.Error);
			}
		}

		/// <summary>
		/// Called when time table for the current group loaded.
		/// </summary>
		/// <param name="groupId">Group identifier for which the time table is loaded.</param>
		/// <param name="response">The facade response.</param>
		private void OnGroupTimeTableLoaded(string groupId, FacadeResponse<GroupTimeTable> response)
		{
			bool canUpdate = ((Group != null) && (Group.Id == groupId));
			if (canUpdate)
			{
				if (response.DataSource == FacadeResponseDataSource.Server)
				{
					if (IsTimeTableFromServerLoaded)
					{
						return;
					}
					IsTimeTableFromServerLoaded = true;
					UpdateIsBusy();
				}
				bool isSuccess = (response.IsSuccess && (response.Data != null));
				if (isSuccess)
				{
					EvenWeek = new PeriodicWeekItemVM(PeriodicWeekType.Even);
					EvenWeek.Days.AddRange(
						response.Data.EvenWeek.Days
						.Where(dayModel => dayModel.Classes != null && dayModel.Classes.Count > 0)
						.Select(dayModel => new PeriodicWeekDayItemVM(dayModel.DayOfWeek, dayModel.Classes)));
					OddWeek = new PeriodicWeekItemVM(PeriodicWeekType.Odd);
					OddWeek.Days.AddRange(
						response.Data.OddWeek.Days
						.Where(dayModel => dayModel.Classes != null && dayModel.Classes.Count > 0)
						.Select(dayModel => new PeriodicWeekDayItemVM(dayModel.DayOfWeek, dayModel.Classes)));
				}
				else
				{
					DisplayError(response.DataSource, "расписания", response.Error);
				}
			}
		}

		/// <summary>
		/// Updates the is busy status.
		/// </summary>
		private void UpdateIsBusy()
		{
			IsBusy = !IsTimeTableFromServerLoaded;
		}

		/// <summary>
		/// Executes the favorite command, change the 'FavoriteState' of the group item.
		/// </summary>
		/// <param name="groupItem">The group item.</param>
		private void ExecuteFavoriteCommand(GroupItemVM groupItem)
		{
			if (groupItem.FavoriteState == FavoriteState.NotFavorite)
			{
				groupItem.FavoriteState = FavoriteState.ChangingToFavorite;
				FavoriteCommand.RaiseCanExecuteChanged();
				_structureFacade.AddGroupToFavoritesAsync(groupItem.Id, response =>
				{
					groupItem.FavoriteState = response.IsSuccess ? FavoriteState.Favorite : FavoriteState.NotFavorite;
					FavoriteCommand.RaiseCanExecuteChanged();
				});
			}
			else if (groupItem.FavoriteState == FavoriteState.Favorite)
			{
				groupItem.FavoriteState = FavoriteState.ChangingToNotFavorite;
				FavoriteCommand.RaiseCanExecuteChanged();
				_structureFacade.RemoveGroupFromFavoritesAsync(groupItem.Id, response =>
				{
					groupItem.FavoriteState = response.IsSuccess ? FavoriteState.NotFavorite : FavoriteState.Favorite;
					FavoriteCommand.RaiseCanExecuteChanged();
				});
			}
		}

		/// <summary>
		/// Determines whether this instance can execute favorite command, for the specified group item.
		/// </summary>
		/// <param name="groupItem">The group item.</param>
		private bool CanExecuteFavoriteCommand(GroupItemVM groupItem)
		{
			bool result =
				groupItem != null &&
				!string.IsNullOrEmpty(groupItem.Id) && (
					groupItem.FavoriteState == FavoriteState.Favorite ||
					groupItem.FavoriteState == FavoriteState.NotFavorite);
			return result;
		}
	}
}
